﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Zhy.DigitaPlatform.DeviceAccess.Base;
using Zhy.DigitaPlatform.Entities;

namespace Zhy.DigitaPlatform.DeviceAccess.Transfer
{
    /// <summary>
    /// TCP通讯类
    /// </summary>
    internal class SocketTcpUnit : TransferObject
    {
        /// <summary>
        /// TCP通讯对象
        /// </summary>
        public Socket socket; 

        /// <summary>
        /// 初始化
        /// </summary>
        public SocketTcpUnit()
        {
            // 实例化通讯对象
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this.TUnit = socket;
        }

        /// <summary>
        /// IP地址
        /// </summary>
        string ip;
        /// <summary>
        /// 端口号
        /// </summary>
        int port = 0;

        /// <summary>
        /// 通讯参数配置
        /// </summary>
        /// <param name="props">设备的组件通讯属性集合</param>
        /// <returns></returns>
        internal override Result Config(List<DevicePropItemEntity> props)
        {
            try
            {
                // 获取IP地址
                ip = props.FirstOrDefault(p => p.PropName == "IP")?.PropValue;
                // 获取端口号
                int.TryParse(props.FirstOrDefault(p => p.PropName == "Port")?.PropValue, out port);

                return new Result();
            }
            catch (Exception ex)
            {
                return new Result(false, ex.Message);
            }
        }

        /// <summary>
        /// 超时时间
        /// </summary>

        ManualResetEvent TimeoutObject = new ManualResetEvent(false);

        /// <summary>
        /// 锁
        /// </summary>
        readonly object socketLock = new object();

        /// <summary>
        /// 连接
        /// </summary>
        /// <param name="trycount">重试次数</param>
        /// <returns></returns>
        internal override Result Connect(int trycount = 30)
        {
            lock (socketLock)
            {
                Result result = new Result();

                try
                {
                    if (socket == null)
                        // ProtocolType 可支持配置
                        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                    int count = 0;
                    bool connectState = false;
                    TimeoutObject.Reset();
                    while (count < trycount)
                    {
                        if (!(!socket.Connected || (socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
                        {   // 判断设备的通讯情况-没法确定真实通讯情况
                            return result;
                        }
                        try
                        {
                            socket?.Close();
                            socket.Dispose();
                            socket = null;
                            // 实例化通讯对象
                            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
                            // 异步连接
                            socket.BeginConnect(ip, port, callback =>
                            {
                                connectState = false;
                                var cbSocket = callback.AsyncState as Socket;
                                if (cbSocket != null)
                                {
                                    connectState = cbSocket.Connected;

                                    if (cbSocket.Connected)
                                        cbSocket.EndConnect(callback);

                                }
                                TimeoutObject.Set();
                            }, socket);
                            TimeoutObject.WaitOne(2000, false);
                            if (!connectState) throw new Exception();
                            else break;
                        }
                        catch (SocketException ex)
                        {
                            if (ex.SocketErrorCode == SocketError.TimedOut)// 连接超时
                                throw new Exception(ex.Message);
                        }
                        catch (Exception) { }
                    }
                    if (socket == null || !socket.Connected || ((socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
                    {
                        throw new Exception("网络连接失败");
                    }
                }
                catch (Exception ex)
                {
                    result = new Result(false, ex.Message);
                }

                return result;
            }
        }

        /// <summary>
        /// 发送和读取数据
        /// </summary>
        /// <param name="req">要发送的数据</param>
        /// <param name="header_len">头部的报文长度</param>
        /// <param name="timeout">超时时间</param>
        /// <param name="calcLen">计算实际报文长度逻辑-把头部报文传进去，根据头部计算实际报文的长度</param>
        /// <returns></returns>
        internal override Result<List<byte>> SendAndReceived(List<byte> req, int header_len, int timeout, Func<byte[], int> calcLen)
        {
            lock (socketLock)
            {
                Result<List<byte>> result = new Result<List<byte>>(); // 接收报文数据集合
                try
                {
                    socket.ReceiveTimeout = timeout; // 设置超时时间

                    if (req != null) // 发送报文数据
                        socket.Send(req.ToArray(), 0, req.Count, SocketFlags.None); 

                    // 获取报文头字节
                    byte[] data = new byte[header_len]; // 要接收的报文头
                    socket.Receive(data, 0, header_len, SocketFlags.None); // 接收报文数据
                    result.Data = new List<byte>(data); // 接收到的报文头部数据

                    int dataLen = 0;
                    if (calcLen != null)
                        dataLen = calcLen(data); // 计算实际报文数据长度
                    if (dataLen == 0)
                        throw new Exception("获取数据长度失败");

                    // 剩余的报文字节
                    data = new byte[dataLen]; // 实例化要接收的实际报文集合
                    socket.Receive(data, 0, dataLen, SocketFlags.None); // 读取实际报文数据
                    result.Data.AddRange(data); // 添加实际报文数据
                }
                catch (SocketException se)
                {
                    result.Status = false;
                    if (se.SocketErrorCode == SocketError.TimedOut) 
                    {  // 超时的异常捕获
                        result.Message = "未获取到响应数据，接收超时";
                    }
                }
                catch (Exception ex)
                {
                    result.Status = false;
                    result.Message = ex.Message;
                }
                return result; // 返回通讯数据
            }
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        internal override Result Close()
        {
            try
            {
                if (socket?.Connected ?? false)
                    socket?.Shutdown(SocketShutdown.Both);//正常关闭连接
                base.Close();
            }
            catch { }

            try
            {
                socket?.Close();
                return new Result();
            }
            catch (Exception ex) { return new Result(false, ex.Message); }
        }
    }

    /// <summary>
    /// UDP通讯类
    /// </summary>
    internal class SocketUdpUnit : TransferObject
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="props"></param>
        /// <returns></returns>
        internal override Result Config(List<DevicePropItemEntity> props)
        {
            return base.Config(props);
        }
    }
}
